package br.com.iqt.midlleware;

import br.com.iqt.*;
import br.com.iqt.exception.DbmsException;
import br.com.iqt.exception.HeuristicsSelectedException;
import br.com.iqt.exception.SqlInputException;
import br.com.iqt.zql.ParseException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;

/**
 * 
 * @author Arlino
 */
public class Midlleware {
    private Dbms dbms;
    private AgentRewriter ar;
    /* Tipo de cash a ser utilizado: AgentRewriter.noCash (sem cash), 
     * AgentRewriter.sqlCash (cash de SQL) ou AgentRewriter.heuristicCash (cash de heurísticas).*/
    private int cashType;
    private ArrayList listHeuristicsCashed;
    private Connection connection;
    
    /**
     * Instancia um objeto Midlleware com um objeto DBMS.
     * @param dbms
     * Possui as informações de acesso ao SGBD.
     */
    public Midlleware(Dbms dbms) throws ClassNotFoundException, SQLException {
        this.dbms = dbms;
        cashType = AgentRewriter.noCash;
        this.ar = new AgentRewriter(dbms);
        this.listHeuristicsCashed = null;
        this.connection = ConnectionDbms.getConnection(dbms);
    }
    
    /**
     * Instancia um objeto Midlleware com um objeto DBMS.
     * @param dbms
     * Possui as informações de acesso ao SGBD.
     */
    public Midlleware(Dbms dbms, int cashType) throws FileNotFoundException, IOException, ClassNotFoundException, SQLException {
        this.dbms = dbms;
        this.cashType = cashType;
        this.ar = new AgentRewriter(dbms);
        if (cashType != AgentRewriter.noCash) {
            this.listHeuristicsCashed = AgentRewriter.getHeuristicsFromCash();
            if (this.listHeuristicsCashed == null) {
                this.listHeuristicsCashed = new ArrayList();
            }
        }
        this.connection = ConnectionDbms.getConnection(dbms);
    }
    
    public void close() throws SQLException{
        this.connection.close();
    }
    
    /**
     * 
     * @param sql
     * @return
     * @throws ClassNotFoundException
     * @throws SQLException
     * @throws ParseException
     * @throws SqlInputException
     */
    public ResultSet executeQuery(String sql) throws SqlInputException, java.text.ParseException, ParseException, ClassNotFoundException, SQLException, FileNotFoundException, IOException, DbmsException, HeuristicsSelectedException{
        HeuristicsSelected heuristicsSelected = null;
        //informa se houve uso da cash
        boolean cashUsed = false;
        String rewritedSql = sql;
        boolean isRewrited = false;
        
        switch (this.cashType) {
            //Se nao for utiliza cash, tenta reescrever utilizando todas as heuristicas disponiveis
            case AgentRewriter.noCash:
                rewritedSql = ar.analyseStatement(sql.toString());
                cashUsed = false;
                isRewrited = ar.isRewrited();
                break;
            //Se utilizar a cash de sqls reescritas, tenta obter uma sql na cash. Se nao consguir, 
            //tenta reescrever utilizando todas as heuristicas disponiveis
            case AgentRewriter.sqlCash:
                rewritedSql = AgentRewriter.getSqlRewritedCashed(sql.toString(), listHeuristicsCashed);
                if (rewritedSql != null) {
                    cashUsed = true;
                    isRewrited = true;
                } else {
                    cashUsed = false;
                    rewritedSql = ar.analyseStatement(sql.toString());
                    isRewrited = ar.isRewrited();
                }
                break;
            //Se utilizar a cash de heuristicas, tenta obter as heuristicas na cash. Se nao consguir, 
            //tenta reescrever utilizando todas as heuristicas disponiveis
            case AgentRewriter.heuristicCash:
                heuristicsSelected = AgentRewriter.getHeuristicCashed(sql.toString(), listHeuristicsCashed);
                if (heuristicsSelected != null) {
                    cashUsed = true;
                } else {
                    cashUsed = false;
                    heuristicsSelected = null;
                }
                rewritedSql = ar.analyseStatement(sql.toString(), heuristicsSelected);
                isRewrited = ar.isRewrited();
                break;
        }

        //se tiver conseguido reescrever uma consulta que não está na cash, entao a armazena na cash
        if ((this.cashType != AgentRewriter.noCash) && (!cashUsed) && (isRewrited)) {
            heuristicsSelected = ar.getHeuristicsSelected();
            AgentRewriter.addHeuristicCashed(sql.toString(), rewritedSql, heuristicsSelected);

            //adiciona a nova heuristica ao array lista sqls vindas do cash
            HeuristicsForSql HeuristicsForSql = new HeuristicsForSql(sql.toString(), rewritedSql, heuristicsSelected);
            listHeuristicsCashed.add(HeuristicsForSql);
        }
        
        ResultSet resultSet = ConnectionDbms.executeQuery(this.connection, rewritedSql);
        return resultSet;
    }
    
    /**
     * 
     * @param sql
     * @return
     * @throws ClassNotFoundException
     * @throws SQLException
     * @throws ParseException
     * @throws SqlInputException
     */
    public int executeUpdate(String sql) throws ClassNotFoundException, SQLException, ParseException, SqlInputException, java.text.ParseException, FileNotFoundException, IOException, DbmsException, HeuristicsSelectedException{
        HeuristicsSelected heuristicsSelected = null;
        //informa se houve uso da cash
        boolean cashUsed = false;
        String rewritedSql = sql;
        boolean isRewrited = false;
        
        switch (this.cashType) {
            //Se nao for utiliza cash, tenta reescrever utilizando todas as heuristicas disponiveis
            case AgentRewriter.noCash:
                rewritedSql = ar.analyseStatement(sql.toString());
                cashUsed = false;
                isRewrited = ar.isRewrited();
                break;
            //Se utilizar a cash de sqls reescritas, tenta obter uma sql na cash. Se nao consguir, 
            //tenta reescrever utilizando todas as heuristicas disponiveis
            case AgentRewriter.sqlCash:
                rewritedSql = AgentRewriter.getSqlRewritedCashed(sql.toString(), listHeuristicsCashed);
                if (rewritedSql != null) {
                    cashUsed = true;
                    isRewrited = true;
                } else {
                    cashUsed = false;
                    rewritedSql = ar.analyseStatement(sql.toString());
                    isRewrited = ar.isRewrited();
                }
                break;
            //Se utilizar a cash de heuristicas, tenta obter as heuristicas na cash. Se nao consguir, 
            //tenta reescrever utilizando todas as heuristicas disponiveis
            case AgentRewriter.heuristicCash:
                heuristicsSelected = AgentRewriter.getHeuristicCashed(sql.toString(), listHeuristicsCashed);
                if (heuristicsSelected != null) {
                    cashUsed = true;
                } else {
                    cashUsed = false;
                    heuristicsSelected = null;
                }
                rewritedSql = ar.analyseStatement(sql.toString(), heuristicsSelected);
                isRewrited = ar.isRewrited();
                break;
        }

        //se tiver conseguido reescrever uma consulta que não está na cash, entao a armazena na cash
        if ((this.cashType != AgentRewriter.noCash) && (!cashUsed) && (isRewrited)) {
            heuristicsSelected = ar.getHeuristicsSelected();
            AgentRewriter.addHeuristicCashed(sql.toString(), rewritedSql, heuristicsSelected);

            //adiciona a nova heuristica ao array lista sqls vindas do cash
            HeuristicsForSql HeuristicsForSql = new HeuristicsForSql(sql.toString(), rewritedSql, heuristicsSelected);
            listHeuristicsCashed.add(HeuristicsForSql);
        }

        int rowCount = ConnectionDbms.executeUpdate(this.connection, rewritedSql);
        return rowCount;
    }
    
    public void clearCash() throws IOException{
        AgentRewriter.clearCash();
    }
    
    /**
     * 
     * @return
     */
    public boolean isRewrited(){
        return this.ar.isRewrited();
    }
    
    /**
     * 
     * @return
     */
    public boolean isError(){
        return this.ar.isError();
    }
    
    /**
     * 
     * @return
     */
    public int getCount(){
        return this.ar.getCount();
    }
    
    /**
     * 
     * @return
     */
    public double getTimeMillis(){
        return this.ar.getTimeMillis();
    }
    
    /**
     * 
     * @return
     */
    public double getTimeNano(){
        return this.ar.getTimeNano();
    }
    
    /**
     * 
     * @return
     */
    public double getTimeSecond(){
        return this.ar.getTimeSecond();
    }
    
    /**
     * 
     * @return
     */
    public Collection getSqlsTransformationList(){
        return this.ar.getSqlsTransformationList();
    }
    
    /**
     * 
     * @return
     */
    public Collection<Transformation> getTransformationList(){
        return this.ar.getTransformationList();
    }
}
